
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace Rmvbediter
{

    public class RMMF
    {
        public Index Index = null;

        public Data Data = null;

        public int GetBeforTimeSpan(int timespan)
        {
            if (timespan == 0)
                return 0;

            int last = 0;
            foreach (DataPacket dp in Data.keyFrame)
            {
                if (dp.timestamp > timespan)
                    return last;
                last = dp.timestamp;
            }

            return last;
        }

        public bool Load(string fileName)
        {
            if (!File.Exists(fileName))
                return false;

            using (BSReader reader = new BSReader(fileName, FileMode.Open, FileAccess.ReadWrite, true))
            {
                RMF head = (RMF)reader.Read(typeof(RMF));
                PROP prop = (PROP)reader.Read(typeof(PROP));
                Console.WriteLine(reader.Position);
                MDPR mdpr = GetMDPR(reader);
                Console.WriteLine(mdpr.ToString());
                MDPR mdpr2 = GetMDPR(reader);
                Console.WriteLine(mdpr2.ToString());
                MDPR mdpr3 = GetMDPR(reader);
                Console.WriteLine(mdpr3.ToString());
                CONT cont = GetCONT(reader);
                Console.WriteLine(cont.ToString());
                Index index = GetIndex(reader, prop.index_offset);
                this.Index = index;
                Console.WriteLine(index.keyHead);
                for (int i = 0; i < index.KeyIndexs.Count; i++)
                {
                    Console.WriteLine(index.KeyIndexs[i]);
                }

                Data data = GetData(reader, prop.data_offset);

                this.Data = data;

                reader.Close();
            }

            return true;
        }
        public Index GetIndex(BSReader reader, long offset)
        {
            Index ret = new Index();
            reader.Position = offset;
            ret.head = (IndexHead)reader.Read(typeof(IndexHead));
            reader.Position = ret.head.next_index_header;
            ret.keyHead = (IndexHead)reader.Read(typeof(IndexHead));
            for (int i = 0; i < ret.keyHead.num_indices; i++)
                ret.KeyIndexs.Add((IndexDate)reader.Read(typeof(IndexDate)));
            return ret;
        }
        public Data GetData(BSReader reader, long offset)
        {
            Data ret = new Data();
            reader.Position = offset;
            ret.head = (DataHeader)reader.Read(typeof(DataHeader));
            long nextOffset = reader.Position;
            for (int i = 0; i < ret.head.num_packets; i++)
            {
                reader.Position = nextOffset;
                DataPacket dp = (DataPacket)reader.Read(typeof(DataPacket));
                if (dp.stream_number == 0)
                {
                    ret.keyFrame.Add(dp);
                   // Console.WriteLine(dp);
                }
                nextOffset += dp.length;
            }
            return ret;
        }
        private static MDPR GetMDPR(BSReader reader)
        {
            long p = reader.Position;
            MDPR mdpr = new MDPR((MDPRBase)reader.Read(typeof(MDPRBase)));
            mdpr.stream_name_size = (byte)reader.ReadByte();
            mdpr.stream_name = new byte[mdpr.stream_name_size];
            reader.Read(mdpr.stream_name, 0, mdpr.stream_name_size);
            mdpr.streamName = Encoding.Default.GetString(mdpr.stream_name);
            mdpr.mime_type_size = (byte)reader.ReadByte();
            mdpr.mime_type = new byte[mdpr.mime_type_size];
            reader.Read(mdpr.mime_type, 0, mdpr.mime_type_size);
            mdpr.mimeType = Encoding.Default.GetString(mdpr.mime_type);
            mdpr.type_specific_len = reader.ReadIntType(4);
            mdpr.type_specific_data = new byte[mdpr.type_specific_len];
            //reader.Read(mdpr.type_specific_data, 0, mdpr.type_specific_len);
            if (reader.Position - p < mdpr.size)
                reader.Position = p + mdpr.size;
            return mdpr;
        }
        private static CONT GetCONT(BSReader reader)
        {
            CONT cont = new CONT((CONTBase)reader.Read(typeof(CONTBase)));
            cont.Title = ReadString(reader, 2);
            cont.Author = ReadString(reader, 2);
            cont.CopyRight = ReadString(reader, 2);
            cont.Comment = ReadString(reader, 2);
            return cont;
        }
        static void ReadString(BSReader reader, int size, out int len, out byte[] buf, out string str)
        {
            len = reader.ReadIntType(size);
            buf = new byte[len];
            reader.Read(buf, 0, len);
            str = Encoding.Default.GetString(buf);
        }
        static StructString ReadString(BSReader reader, int size)
        {
            StructString ret = new StructString();
            ret.Len = reader.ReadIntType(size);
            ret.buf = new byte[ret.Len];
            reader.Read(ret.buf, 0, ret.Len);
            ret.String = Encoding.Default.GetString(ret.buf);
            return ret;
        }
    }
    #region RMF

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct RMF
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] objID;
        [MarshalAs(UnmanagedType.I4)]
        public Int32 size;
        [MarshalAs(UnmanagedType.I2)]
        public Int16 version;
        [MarshalAs(UnmanagedType.I4)]
        public Int32 fileVersion;
        [MarshalAs(UnmanagedType.I4)]
        public Int32 num_header;
    }
    #endregion
    #region PROP
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct PROP
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] objID;
        public Int32 size;
        public Int16 version;
        public Int32 max_bit_rate;
        public Int32 avg_bit_rate;
        public Int32 max_packet_size;
        public Int32 avg_packet_size;
        public Int32 num_packets;
        public Int32 duration;
        public Int32 preroll;
        public Int32 index_offset;
        public Int32 data_offset;
        public Int16 num_streams;
        public Int16 flags;

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("objID\t{0}", objID);
            sb.AppendLine();
            sb.AppendFormat("size\t{0}", size);
            sb.AppendLine();
            sb.AppendFormat("version\t{0}", version);
            sb.AppendLine();
            sb.AppendFormat("max_bit_rate\t{0}", max_bit_rate);
            sb.AppendLine();
            sb.AppendFormat("avg_bit_rate\t{0}", avg_bit_rate);
            sb.AppendLine();
            sb.AppendFormat("max_packet_size\t{0}", max_packet_size);
            sb.AppendLine();
            sb.AppendFormat("avg_packet_size\t{0}", avg_packet_size);
            sb.AppendLine();
            sb.AppendFormat("num_packets\tnum_packets{0}", num_packets);
            sb.AppendLine();
            sb.AppendFormat("duration\t{0}", duration);
            sb.AppendLine();
            sb.AppendFormat("preroll\t{0}", preroll);
            sb.AppendLine();
            sb.AppendFormat("index_offset\t{0}", index_offset);
            sb.AppendLine();
            sb.AppendFormat("data_offset\t{0}", data_offset);
            sb.AppendLine();
            sb.AppendFormat("num_streams\t{0}", num_streams);
            sb.AppendLine();
            sb.AppendFormat("flags\t{0}", flags);
            sb.AppendLine();
            return sb.ToString();
        }
    }
    #endregion
    #region MDPR
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct MDPRBase
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] objID;
        public Int32 size;
        public Int16 object_version;
        public Int16 stream_number;
        public Int32 max_bit_rate;
        public Int32 avg_bit_rate;
        public Int32 max_packet_size;
        public Int32 avg_packet_size;
        public Int32 start_time;
        public Int32 preroll;
        public Int32 duration;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("objID:{0}", objID);
            builder.AppendLine();
            builder.AppendFormat("size:{0}", size);
            builder.AppendLine();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("stream_number:{0}", stream_number);
            builder.AppendLine();
            builder.AppendFormat("max_bit_rate:{0}", max_bit_rate);
            builder.AppendLine();
            builder.AppendFormat("avg_bit_rate:{0}", avg_bit_rate);
            builder.AppendLine();
            builder.AppendFormat("max_packet_size:{0}", max_packet_size);
            builder.AppendLine();
            builder.AppendFormat("avg_packet_size:{0}", avg_packet_size);
            builder.AppendLine();
            builder.AppendFormat("start_time:{0}", start_time);
            builder.AppendLine();
            builder.AppendFormat("preroll:{0}", preroll);
            builder.AppendLine();
            builder.AppendFormat("duration:{0}", duration);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    public class MDPR
    {
        public MDPR(MDPRBase o)
        {
            objID = o.objID;
            size = o.size;
            object_version = o.object_version;
            stream_number = o.stream_number;
            max_bit_rate = o.max_bit_rate;
            avg_bit_rate = o.avg_bit_rate;
            max_packet_size = o.max_packet_size;
            avg_packet_size = o.avg_packet_size;
            start_time = o.start_time;
            preroll = o.preroll;
            duration = o.duration;
        }
        public char[] objID;
        public Int32 size;
        public Int16 object_version;
        public Int16 stream_number;
        public Int32 max_bit_rate;
        public Int32 avg_bit_rate;
        public Int32 max_packet_size;
        public Int32 avg_packet_size;
        public Int32 start_time;
        public Int32 preroll;
        public Int32 duration;
        public byte stream_name_size;
        public byte[] stream_name;
        public string streamName;
        public byte mime_type_size;
        public byte[] mime_type;
        public string mimeType;
        public Int32 type_specific_len;
        public byte[] type_specific_data;
        public string specific;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("objID:{0}", objID);
            builder.AppendLine();
            builder.AppendFormat("size:{0}", size);
            builder.AppendLine();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("stream_number:{0}", stream_number);
            builder.AppendLine();
            builder.AppendFormat("max_bit_rate:{0}", max_bit_rate);
            builder.AppendLine();
            builder.AppendFormat("avg_bit_rate:{0}", avg_bit_rate);
            builder.AppendLine();
            builder.AppendFormat("max_packet_size:{0}", max_packet_size);
            builder.AppendLine();
            builder.AppendFormat("avg_packet_size:{0}", avg_packet_size);
            builder.AppendLine();
            builder.AppendFormat("start_time:{0}", start_time);
            builder.AppendLine();
            builder.AppendFormat("preroll:{0}", preroll);
            builder.AppendLine();
            builder.AppendFormat("duration:{0}", duration);
            builder.AppendLine();
            builder.AppendFormat("stream_name_size:{0}", stream_name_size);
            builder.AppendLine();
            builder.AppendFormat("stream_name:{0}", stream_name);
            builder.AppendLine();
            builder.AppendFormat("streamName:{0}", streamName);
            builder.AppendLine();
            builder.AppendFormat("mime_type_size:{0}", mime_type_size);
            builder.AppendLine();
            builder.AppendFormat("mime_type:{0}", mime_type);
            builder.AppendLine();
            builder.AppendFormat("mimeType:{0}", mimeType);
            builder.AppendLine();
            builder.AppendFormat("type_specific_len:{0}", type_specific_len);
            builder.AppendLine();
            builder.AppendFormat("type_specific_data:{0}", type_specific_data);
            builder.AppendLine();
            builder.AppendFormat("specific:{0}", specific);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    public class StructString
    {
        public int Len;
        public byte[] buf;
        public string String;
        public override string ToString()
        {
            return String;
        }
    }
    #endregion
    #region CONT
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct CONTBase
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] objID;
        public Int32 size;
        public Int16 object_version;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("objID:{0}", objID);
            builder.AppendLine();
            builder.AppendFormat("size:{0}", size);
            builder.AppendLine();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();

            return builder.ToString();
        }
    }

    public class CONT
    {
        public CONT(CONTBase cont)
        {
            objID = cont.objID;
            size = cont.size;
            object_version = cont.object_version;
        }
        public char[] objID;
        public Int32 size;
        public Int16 object_version;
        public StructString Title;
        public StructString Author;
        public StructString CopyRight;
        public StructString Comment;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("objID:{0}", objID);
            builder.AppendLine();
            builder.AppendFormat("size:{0}", size);
            builder.AppendLine();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("Title:{0}", Title);
            builder.AppendLine();
            builder.AppendFormat("Author:{0}", Author);
            builder.AppendLine();
            builder.AppendFormat("CopyRight:{0}", CopyRight);
            builder.AppendLine();
            builder.AppendFormat("Comment:{0}", Comment);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    #endregion
    #region Index
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct IndexHead
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] objID;
        public Int32 size;
        public Int16 object_version;
        public Int32 num_indices;
        public Int16 stream_number;
        public Int32 next_index_header;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("objID:{0}", objID);
            builder.AppendLine();
            builder.AppendFormat("size:{0}", size);
            builder.AppendLine();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("num_indices:{0}", num_indices);
            builder.AppendLine();
            builder.AppendFormat("stream_number:{0}", stream_number);
            builder.AppendLine();
            builder.AppendFormat("next_index_header:{0}", next_index_header);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct IndexDate
    {
        public Int16 object_version;
        public Int32 timestamp;
        public Int32 offset;
        public Int32 packet_count_for_this_packet;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("timestamp:{0}", timestamp);
            builder.AppendLine();
            builder.AppendFormat("offset:{0}", offset);
            builder.AppendLine();
            builder.AppendFormat("packet_count_for_this_packet:{0}", packet_count_for_this_packet);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    public class Index
    {
        public IndexHead head;
        public IndexHead keyHead;
        public List<IndexDate> KeyIndexs = new List<IndexDate>();
        public List<IndexDate> Indexs = new List<IndexDate>();
    }

    #endregion
    #region Data
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DataHeader
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] objID;
        public Int32 size;
        public Int16 object_version;
        public Int32 num_packets;
        public Int32 next_data_header;

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("objID:{0}", objID);
            builder.AppendLine();
            builder.AppendFormat("size:{0}", size);
            builder.AppendLine();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("num_packets:{0}", num_packets);
            builder.AppendLine();
            builder.AppendFormat("next_data_header:{0}", next_data_header);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DataPacket
    {
        public Int16 object_version;
        public Int16 length;
        public Int16 stream_number;
        public Int32 timestamp;
        public byte reserved;
        public byte flags;
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("object_version:{0}", object_version);
            builder.AppendLine();
            builder.AppendFormat("length:{0}", length);
            builder.AppendLine();
            builder.AppendFormat("stream_number:{0}", stream_number);
            builder.AppendLine();
            builder.AppendFormat("timestamp:{0}", timestamp);
            builder.AppendLine();
            builder.AppendFormat("reserved:{0}", reserved);
            builder.AppendLine();
            builder.AppendFormat("flags:{0}", flags);
            builder.AppendLine();

            return builder.ToString();
        }
    }
    public class Data
    {
        public DataHeader head;
        public List<DataPacket> keyFrame = new List<DataPacket>();
    }

    #endregion
}
